#include <stdio.h>
#include <iostream>
#include <fstream>
#include <cstdlib>

#include "global.h"

//      GLOBAL CONSTANTS

int verbose=0;                                      // WHAT TO PRINT OUT

double drand48() {
	return 1.f;
}

//      INPUT FILE CONTAINING RANDOM OBJECTS

const char* randomfilename="random.pov";

int nrandom=0;                          // NUMBER OF RANDOM OBJECTS
double rho=.0001;                       // DEFAULT DENSITY OF RANDOM SPHERES

double random(double a, double b) {                     // a<=random(a,b)<=b
//      double r=(double)rand()/(double)RAND_MAX;       // 0.<=r<=1.
        double r=drand48();                             // 0.<=r<=1.
        return a+r*(b-a);
}

void randomobjects(const char* filename) {                // GENERATE RANDOM SPHERES
        std::ofstream o(filename, std::ios::out);
        o<<"#declare WHITE=color rgb<1,1,1>\n";
        o<<"#declare BLACK=color rgb<0,0,0>\n";
        o<<"#declare S=sphere{<0,0,0>,1 pigment{color WHITE}finish{diffuse 1}}\n";
        o<<"\n";
        o<<"camera{location<0,0,0> direction<0,0,1> right<1,0,0> up<0,1,0>}\n";
        o<<"light_source{<0,0,0> color WHITE}\n";
        o<<"light_source{<0,0,0> color WHITE}\n";
        o<<"background {color BLACK}\n";
        o<<"\n";
        double h=pow(nrandom/rho, 1./3.)/2.;        // HALF WIDTH OF SCENE CUBE
        for(int i=0; i<nrandom; i++) {
                o<<"object{S "<<"translate<";
                o<<random(-h,h)<<","<<random(-h,h)<<","<<random(0.,2*h);
                o<<">}\n";
        }
}

//      EXTERNALS

extern FILE* inputfile;                             // DEFINED IN THE lex FILE
extern const char* inputfilename;                         // DEFINED IN THE lex FILE
extern int yyparse(void);                           // GENERATED BY yacc FILE
extern intensity background;                        // DEFINED IN yacc FILE

//      DEBUGGING

#ifdef COUNTMEM
extern "C" {void* malloc(size_t); void free(void*);}
long nbytes=0L;
void *operator new(size_t size) {
        nbytes+=size;
        void *p=malloc(size+8); std::cout<<nbytes<<"\n";
        *(long*)p=size;
        return p+8;
}
void operator delete(void *p) {
        p-=8;
        nbytes-=*(long*)p;
        free(p);
}
#endif

//      PICTURE BUFFER

const int MAXRES=1024;
icolor buf[MAXRES];                                 // ONE ROW OF R,G,B BYTES
int xres=200, yres=200;                             // ACTUAL RESOLUTION

//      OUTPUT FILE

const char* outputfilename="picture";                     // DEFAULT OUTPUT NAME

void outinit(int xres, int yres) {                  // INITIALIZE OUTPUT FILE
        FILE *f;
        if(!(f=fopen(outputfilename,"wb")))
                return;
        fwrite((void*)&xres, sizeof(int), 1, f);
        fwrite((void*)&yres, sizeof(int), 1, f);
        fclose(f);
}

void output(icolor b[]) {                           // OUTPUT ONE ROW
        FILE *f;
        if(!(f=fopen(outputfilename,"ab")))
                return;
        fwrite((void*)b, sizeof(icolor), xres, f);
        fclose(f);

}

//      INTERSECTION ACCELERATION METHODS

method* query;                                  // ACCELERATION METHOD

//      RECURSIVE RAY TRACING

int lmax=4;                                     // MAXIMAL LEVEL OF RECURSION

intensity trace(const ray& r) {
        if(r.l>lmax)
                return background;
        intersect i=(*query)(r);
        if(!i.o) 
                return background;
        return i.o->shade(r,i);
}

//      MAIN PROGRAM

void usage() {
        std::cout<<"usage: oopov [SWITCHes] {FILENAME | -r #}\n";
        std::cout<<"where\n";
        std::cout<<"    SWITCH can be\n";
        std::cout<<"        -a v             :accelerate via Voronoi-diagram\n";
        std::cout<<"        -a b             :brute-force intersection (default)\n";
        std::cout<<"        -o FILENAME      :name of output file\n";
        std::cout<<"        -r #             :creates # number of random objects\n";
        std::cout<<"                          (also writes them into random.pov)\n";
        std::cout<<"        -r -1            :reads objects from random.pov\n";
        std::cout<<"        -v               :verbose printout\n";
        std::cout<<"        -x #             :horizontal resolution of image\n";
        std::cout<<"        -y #             :vertical resolution of image\n";
        std::cout<<"    # is an integer\n";
        exit(0);
}

int main(int argc, char** argv) {
        query=new method;                               // DEFAULT BRUTE FORCE
        while(argv[1] && argv[1][0]=='-') {             // PROCESS ARGUMENTS
                switch(argv[1][1]) {
                    case 'a':                           // ACCELERATION METHOD
                        switch(argv[2][0]) {
                            case 'B': case 'b':         // BRUTE-FORCE
                                delete query;
                                query=new method;
                                break;
                            case 'V': case 'v':         // VIA VORONOI-DIAGRAM
                                delete query;
                                query=new voronoi;
                                break;
                        }
                        argc--; argv++;
                        break;
                    case 'o':                           // OUTPUT FILE NAME
                        outputfilename=argv[2];
                        argc--; argv++;
                        break;
                    case 'r':                           // USE RANDOM OBJECTS
                        nrandom=atoi(argv[2]);
                        argc--; argv++;
                        break;
                    case 'v':                           // VERBOSE OUTPUT
                        verbose=1;
                        break;
                    case 'x':                           // X-RESOLUTION
                        xres=atoi(argv[2]);
                        argc--; argv++;
                        break;
                    case 'y':                           // Y-RESOLUTION
                        yres=atoi(argv[2]);
                        argc--; argv++;
                        break;
                }
                argc--;
                argv++;
        }
        if(argc>1)                                      // NAME OF INPUT FILE
                inputfilename=argv[1];                  // COMES AS ARGUMENT
        else if(nrandom>0) {
                inputfilename=randomfilename;           // USE RANDOM OBJECTS
                randomobjects(inputfilename);           // GENERATE OBJECTS
        } else if(nrandom<0)
                inputfilename=randomfilename;           // USE RANDOM OBJECTS
        else
                usage();                                // NOT GIVEN -> QUIT
        inputfile=fopen(inputfilename, "r");            // OPEN INPUT FILE
        if(yyparse()!=0)                                // READ INPUT FILE
                exit(1);
        fprintf(stderr, "\n");
        query->preprocess(&objects);                    // PREPROCESS OBJECTS
        outinit(xres, yres);                            // INITIALIZE OUTPUT
        for(int i=0; i<yres; i++) {            // TRACE
                for(int j=0; j<xres; j++) {
                        double x=(2*j-xres+0.5)/xres;
                        double y=(yres-2*i-0.5)/yres;
                        intensity I=trace(actcamera.getray(x,y));
                        buf[j]=(icolor)I;
                }
                output(buf);
                fprintf(stderr, "rows remained: %4d\r", yres-i-1);
        }
        fprintf(stderr, "\n");
        exit(0);
}
